package com.gzsxy.esjy.util;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 反射工具类，集成反射方法、属相缓存
 * @author zhangxiaolong
 * @date 2022/7/6 9:36
 */
public class ReflectUtil {
	
	private final static Logger log = LoggerFactory.getLogger(ReflectUtil.class) ;
	
	/**
	 * Bean方法的缓存
	 */
	private final static Map<String,Map<String,Method>> METHOD_MAP = new ConcurrentHashMap<String,Map<String,Method>>() ;
	
	/**
	 * Bean有效字段缓存
	 */
	private final static Map<String,List<Field>> PARAM_NAME_MAP = new ConcurrentHashMap<String,List<Field>>() ;
	
	/**
	 * 得到方法(所有方法)
	 * @param obj
	 * @param methodName
	 * @param types
	 * @return
	 */
	public static Method getMethod(Object obj,String methodName,Class<?>...types) {
		if(obj == null) {
			log.warn("Object is null");
			return null ;
		}
		Class<? extends Object> clazz = obj.getClass();
		return getMethod(clazz,methodName,types);
	}
	
	/**
	 * 得到方法(所有方法)
	 * @param clazz
	 * @param methodName
	 * @param types
	 * @return
	 */
	public static Method getMethod(Class<?> clazz,String methodName,Class<?>...types) {
		Map<String, Method> map = METHOD_MAP.get(clazz.getName());
		if(map == null) {
			map = new HashMap<String, Method>(10) ;
			METHOD_MAP.put(clazz.getName(), map) ;
		}
		String mapKey = methodName ;
		try {
			if(types.length > 0) {
				mapKey += ".["+JSON.toJSONString(types)+ "]";
			}
			Method method = map.get(mapKey);
			if(method == null) {
				if(types.length > 0) {
					method = clazz.getMethod(methodName,types);
				}else {
					method = clazz.getMethod(methodName);
				}
				map.put(mapKey, method) ;
			}
			return method;
		} catch (Exception e) {
			log.warn(e.getMessage(),e);
		}
		return null;
	}
	
	
	/**
	 * 得到get方法
	 * @param obj
	 * @param fileName
	 * @return
	 */
	public static Method getGETMethod(Object obj,String fileName) {
		if(obj == null) {
			log.warn("Object is null");
			return null ;
		}
		Class<? extends Object> clazz = obj.getClass();
		Map<String, Method> map = METHOD_MAP.get(clazz.getName());
		if(map == null) {
			map = new HashMap<>(10) ;
			METHOD_MAP.put(clazz.getName(), map) ;
		}
		String methodName = creatMethodName("get",fileName);
		try {
			Method method = map.get(methodName);
			if(method == null) {
				method = clazz.getMethod(methodName);
				map.put(methodName, method) ;
			}
			return method;
		} catch (Exception e) {
			log.warn(e.getMessage(),e);
		}
		return null;
	}
	
	/**
	 * 得到字段值
	 * @param obj
	 * @param fileName
	 * @return
	 */
	public static Object getObjectValue(Object obj,String fileName) {
		Method method = getGETMethod(obj,fileName) ;
		return getObjectValue(obj,method);
	}
	
	/**
	 * 得到字段值
	 * @param obj
	 * @param fileName
	 * @param returnType
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getObjectValue(Object obj,String fileName,Class<T> returnType) {
		Method method = getGETMethod(obj,fileName) ;
		return (T) getObjectValue(obj,method);
	}
	
	/**
	 * 得到字段值
	 * @param obj
	 * @param method
	 * @return
	 */
	public static Object getObjectValue(Object obj,Method method) {
		if(method == null || obj == null) {
			return null ;
		}
		try {
			return method.invoke(obj);
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
		return null;
	}
	
	/**
	 * 执行指定方法
	 * @param obj
	 * @param method
	 */
	public static void invoke(Object obj,String fileName,Object... params) {
		Method method = getMethod(obj,fileName) ;
		if(method == null) {
			return ;
		}
		invoke(obj, method, params);
	}
	
	/**
	 * 执行指定方法
	 * @param obj
	 * @param method
	 */
	public static void invoke(Object obj,Method method,Object... params) {
		if(method == null || obj == null) {
			return ;
		}
		try {
			if(params.length > 0) {
				method.invoke(obj,params);
			}else {
				method.invoke(obj);
			}
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
	}
	
	/**
	 * 得到set方法
	 * @param obj
	 * @param fileName
	 * @param types
	 * @return
	 */
	public static Method getSETMethod(Object obj,String fileName,Class<?>[] types) {
		if(obj == null) {
			log.warn("Object is null");
			return null ;
		}
		Class<? extends Object> clazz = obj.getClass();
		Map<String, Method> map = METHOD_MAP.get(clazz.getName());
		if(map == null) {
			map = new HashMap<String, Method>(10) ;
			METHOD_MAP.put(clazz.getName(), map) ;
		}
		String methodName = creatMethodName("set",fileName);
		try {
			Method method = map.get(methodName);
			if(method == null) {
				method = clazz.getMethod(methodName,types);
				map.put(methodName, method) ;
			}
			return method;
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
		return null;
	}
	
	/**
	 * 为字段赋值
	 * @param obj
	 * @param fileName
	 * @param types
	 * @param params
	 * @return
	 */
	public static void setObjectValue(Object obj,String fileName,Class<?>[] types,Object[] params) {
		Method method = getSETMethod(obj,fileName,types) ;
		setObjectValue(obj, method, params);
	}
	
	/**
	 * 为字段赋值
	 * @param obj
	 * @param method
	 * @param params
	 */
	public static void setObjectValue(Object obj,Method method,Object[] params) {
		if(method == null) {
			return ;
		}
		try {
			method.invoke(obj,params);
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
	}

	/**
	 * 获取get与set方法名
	 * @param type
	 * @param name
	 * @return
	 */
	private static String creatMethodName(String type,String name) {
		return type + name.substring(0, 1).toUpperCase() + name.substring(1) ;
	}

	/**
	 * 复制bean
	 * @param from
	 * @param aim
	 */
	public static void copyBean(Object from,Object aim) {
		copyBean(from, aim,false);
	}
	
	/**
	 * 复制bean
	 * @param from
	 * @param aim
	 * @param ignoreNull true:忽略null false:不忽略null
	 */
	public static void copyBean(Object from,Object aim,boolean ignoreNull) {
		if(from == null || aim == null) {
			log.warn("源Bean对象或目标Bean对象为空");
			return ;
		}
		Class<? extends Object> aimClazz = aim.getClass();
		String aimName = aimClazz.getName() ;
		List<Field> list = null ;
		if(PARAM_NAME_MAP.containsKey(aimName)) {
			list = PARAM_NAME_MAP.get(aimName);
			for (Field field : list) {
				Method setMethod = getSETMethod(aim, field.getName(), new Class[]{field.getType()}) ;
				if(setMethod != null) {
					convertValue(from, aim, ignoreNull, field, setMethod);
				}
			}
		}else {
			Set<String> fieldSet = new HashSet<String>();
			list = new ArrayList<Field>() ;
			PARAM_NAME_MAP.put(aimName, list);
			Field[] declaredFields = aimClazz.getDeclaredFields();
			for (Field field : declaredFields) {
				if(Modifier.isStatic(field.getModifiers())){
					continue ;
				}
				Method setMethod = getSETMethod(aim, field.getName(), new Class[]{field.getType()}) ;
				if(setMethod != null) {
					list.add(field) ;	
					fieldSet.add(field.getName()) ;
					convertValue(from, aim, ignoreNull, field, setMethod);
				}
			}
			//处理父类
			Class<?> supclazz = aimClazz.getSuperclass() ;
			while(supclazz != Object.class) {
				declaredFields = supclazz.getDeclaredFields();
				for (Field field : declaredFields) {
					if(Modifier.isStatic(field.getModifiers())){
						continue ;
					}
					if(fieldSet.contains(field.getName())) {
						continue ;
					}
					Method setMethod = getSETMethod(aim, field.getName(), new Class[]{field.getType()}) ;
					if(setMethod != null) {
						list.add(field) ;	
						fieldSet.add(field.getName()) ;
						convertValue(from, aim, ignoreNull, field, setMethod);
					}
				}
				supclazz = supclazz.getSuperclass() ;
			}
		}
	}

	/**
	 * 转换字段值
	 * @param from
	 * @param aim
	 * @param ignoreNull
	 * @param field
	 * @param setMethod
	 */
	private static void convertValue(Object from, Object aim, boolean ignoreNull, Field field, Method setMethod) {
		Method getMethod = getGETMethod(from, field.getName());
		if(getMethod != null) {
			Object value = getObjectValue(from, getMethod) ;
			if(!ignoreNull || value != null) {
				setObjectValue(aim, setMethod, new Object[] {value});
			}
		}
	}
	
	/**
	 * bean转成map
	 * @param bean
	 * @return
	 */
	public static Map<String,Object> convertBeanToMap(Object bean){
		Map<String,Object> map = new HashMap<String, Object>(4) ;
		Class<?> clazz = bean.getClass();
		try {
			Field[] declaredFields = clazz.getDeclaredFields() ;
			for (Field field : declaredFields) {
				String name = field.getName();
				if("serialVersionUID".equals(name)){
					continue ;
				}
				Object obj = getObjectValue(bean,name) ;
				if(obj != null) {
					map.put(name, obj);
				}
			}
			Class<?> supclazz = clazz.getSuperclass() ;
			while(supclazz != Object.class){
				declaredFields = supclazz.getDeclaredFields() ;
				for (Field field : declaredFields) {
					String name = field.getName();
					if("serialVersionUID".equals(name)){
						continue ;
					}
					Object obj = getObjectValue(bean,name) ;
					if(obj != null) {
						map.put(name, obj);
					}
				}
				supclazz  = supclazz.getSuperclass() ;
			}
		} catch (Exception e) {
			log.error("反射初始化异常:{}",e);
		} 
		return map ;
	}
	
	/**
	 * 将javaBean转为另一个javaBean 
	 * 字段名称需要一致 源javaBean 的字段需要包括目标javaBean的字段，字段不存在，忽略
	 * @param bean 源JavaBean
	 * @param clazz 目标javaBean类型
	 * @return
	 */
	public static <T> T convertBeanToBean(Object bean,Class<T> clazz) {
		T resultBean = null ;
		try {
			resultBean = clazz.newInstance() ;
			Field[] declaredFields = clazz.getDeclaredFields() ;
			for (Field field : declaredFields) {
				String name = field.getName();
				if("serialVersionUID".equals(name)){
					continue ;
				}
				Object invokeMethodGet = invokeMethodGet(bean, name) ;
				if(invokeMethodGet != null) {
					invokeMethodSet(resultBean, field, invokeMethodGet) ;
				}
			}
			Class<? super T> supclazz = clazz.getSuperclass() ;
			while(supclazz != Object.class){
				declaredFields = supclazz.getDeclaredFields() ;
				for (Field field : declaredFields) {
					String name = field.getName();
					if("serialVersionUID".equals(name)){
						continue ;
					}
					Object invokeMethodGet = invokeMethodGet(bean, name) ;
					if(invokeMethodGet != null) {
						invokeMethodSet(resultBean, field, invokeMethodGet) ;
					}
				}
				supclazz  = supclazz.getSuperclass() ;
			}
		} catch (InstantiationException e) {
			log.error("反射初始化异常:{}",e);
		} catch (IllegalAccessException e) {
			log.error("反射违法操作异常:{}",e);
		}

		return resultBean;
		
	}

	/**
	 * 反射方法，通过set方法对象属性赋值
	 * @param bean
	 * @param field
	 * @param obj
	 * @return
	 */
	private static Object invokeMethodSet(Object bean, Field field,Object obj){
		String fieldname = field.getName();
		String methodName = "set" + fieldname.substring(0, 1).toUpperCase()
				+ fieldname.substring(1);
		Class<? extends Object> clazz = bean.getClass() ;
		Method method =null;
		try {
			method = clazz.getMethod(methodName,field.getType());
		} catch (Exception e) {
			log.error("反射获取方法异常:{}",e);
			return null ;
		}
		if(method!=null){
			try {
				return method.invoke(bean,obj);
			} catch (Exception e) {
				log.error("反射调用方法异常:{}",e);
				return null;
			}
		}else{
			return null;
		}
	}
	/**
	 * 反射方法，通过get方法获取对象属性
	 * 
	 * @param bean
	 * @param fieldname
	 * @return
	 * @throws Exception
	 */
	public static Object invokeMethodGet(Object bean, String fieldname){

		String methodName = "get" + fieldname.substring(0, 1).toUpperCase()
				+ fieldname.substring(1);
        Class<? extends Object> clazz = bean.getClass() ;
		Method method =null;
		try {
			method = clazz.getMethod(methodName);
		} catch (Exception e) {
			log.error("反射获取方法异常:{}",e);
			return null ;
		}
		if(method!=null){
			try {
				return method.invoke(bean);
			} catch (Exception e) {
				log.error("反射调用方法异常:{}",e);
				return null;
			}
		}else{
			return null;
		}
		
	}
	
	/**
	 * 根据方法名称执行方法
	 * @param bean 执行的类
	 * @param methodName 方法名称
	 * @param paramTypes 参数类型
	 * @param params 参数
	 * @return
	 * @throws Exception 
	 */
	public static Object invokeMethod(Object bean,String methodName,Class<?>[] paramTypes,Object...params) throws Exception{
		
		Class<? extends Object> clazz = bean.getClass() ;
		Method method =null;
		try {
			method = clazz.getMethod(methodName,paramTypes);
			return method.invoke(bean,params);
		} catch (Exception e) {
//			log.error("反射调用方法异常:{}",e);
			throw e ;
		}
		
	}
	
	/**
	 * 将源JavaBean的参数付到目标bean中，参数为空忽略
	 *  字段名称需要一致 源javaBean 的字段需要包括目标javaBean的字段
	 * @param form 源javaBean
	 * @param aim 目标JavaBean
	 */
	public static void editBeanfromBean(Object form,Object aim) {
		Field[] declaredFields = aim.getClass().getDeclaredFields() ;
		for (Field field : declaredFields) {
			String name = field.getName();
			if("serialVersionUID".equals(name)){
				continue ;
			}
			Object invokeMethodGet = invokeMethodGet(form, name) ;
			if(invokeMethodGet != null) {
				invokeMethodSet(aim, field, invokeMethodGet) ;
			}
		}
		Class<?> superclass = aim.getClass().getSuperclass();
		while(superclass != Object.class) {
			declaredFields = superclass.getDeclaredFields() ;
			for (Field field : declaredFields) {
				String name = field.getName();
				if("serialVersionUID".equals(name)){
					continue ;
				}
				Object invokeMethodGet = invokeMethodGet(form, name) ;
				if(invokeMethodGet != null) {
					invokeMethodSet(aim, field, invokeMethodGet) ;
				}
			}
			superclass = superclass.getSuperclass();
		}
		
	}
	
	/**
	 * map转成Bean
	 * @param bean
	 * @param map
	 * @param ignoreNull true 遇到String == null ,替换成空串
	 */
	public static void convertMapToBean(Object bean,Map<String,Object> map,boolean ignoreNull){
		if(bean == null){
			return ;
		}
		Class<? extends Object> clazz = bean.getClass();
		for (String key : map.keySet()) {
			Field field;
			try {
				field = clazz.getDeclaredField(key);
				if(field != null){
					if(map.get(key) ==null && field.getType() == String.class && ignoreNull ){
						invokeMethodSet(bean, field,"");
					}else{
						invokeMethodSet(bean, field,map.get(key));
					}
			    }
			} catch (Exception e) {
				log.error("反射调用方法异常:{}",e);
		    }
		}
	}
	
	/**
	 * map转成Bean
	 * @param map
	 * @param ignoreNull true 遇到String == null ,替换成空串
	 */
	public static Object convertMapToBean(Class<?> clazz,Map<String,Object> map,boolean ignoreNull){
		Object newInstance = null ;
		try {
			newInstance = clazz.newInstance();
			convertMapToBean(newInstance, map, ignoreNull);
		} catch (Exception e) {
			log.error("反射新建对象异常:{}",e);
		}
		return newInstance ;
	}

    /**
     * bean转换为map，只转换有getter setter方法的字段
     * @param bean
     * @return
     */
    public static Map<String, Object> covertBeanToMap(Object bean) {
        if (bean == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        final Class<? extends Object> beanClass = bean.getClass();
        try {
            final BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
            final PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            if (propertyDescriptors == null) {
                return map;
            }
            for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                if (propertyDescriptor == null) {
                    continue;
                }
                final String name = propertyDescriptor.getName();
                Method readMethod = propertyDescriptor.getReadMethod();
                Method writeMethod = propertyDescriptor.getWriteMethod();
                if(readMethod != null && writeMethod != null){
                    Object value = readMethod.invoke(bean);
                    if(value != null){
                        map.put(name, value);
                    }
                }
            }
        } catch (Exception e) {
            log.error("转换map失败", e);
        }
        return map;
    }
	
	/**
	 * map转成Bean
	 * @param bean
	 * @param fileName
	 */
	public static Object getPrivateValue(Object bean,String fileName){
		Class<? extends Object> clazz = bean.getClass();
		Field field = null ;
		try {
			field = clazz.getField(fileName);
		}catch(NoSuchFieldException e) {}
		if(field == null) {
			return null ;
		}
		field.setAccessible(true);
		try {
			return field.get(bean);
		} catch (Exception e) {
			return null ;
		} 
	}

    /**
     * 获取包括父类在内的field
     * @param clazz
     * @return
     */
	public static Set<Field> getAllField(Class<?> clazz){
	    if(clazz == null){
	        throw new RuntimeException("class对象不能为空");
        }
	    Set<Field> fieldSet = new HashSet<>();
        fieldSet.addAll(Arrays.asList(clazz.getDeclaredFields()));
        Class<?> superclass = clazz.getSuperclass();
        while (superclass != Object.class){
            fieldSet.addAll(Arrays.asList(superclass.getDeclaredFields()));
            superclass = superclass.getSuperclass();
        }
        return fieldSet;
    }

	
	/**
	 * Bean转换
	 * @param from
	 * @param aimClazz
	 * @return
	 */
	public static <T> T convertToBean(Object from,Class<T> aimClazz) {
		T aim = null ;
		try {
			aim = aimClazz.newInstance();
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return null ;
		}
		copyBean(from, aim,true);
		return aim;
	}
	
	private final static Set<Class<?>> TYPE_SET = new HashSet<Class<?>>() ;
	
	static {
		TYPE_SET.add(String.class) ;
		TYPE_SET.add(null) ;
		TYPE_SET.add(Integer.class) ;
		TYPE_SET.add(Double.class) ;
		TYPE_SET.add(Float.class) ;
		TYPE_SET.add(Short.class) ;
		TYPE_SET.add(Long.class) ;
		TYPE_SET.add(Byte.class) ;
		TYPE_SET.add(Boolean.class) ;
		TYPE_SET.add(int.class) ;
		TYPE_SET.add(short.class) ;
		TYPE_SET.add(long.class) ;
		TYPE_SET.add(byte.class) ;
		TYPE_SET.add(double.class) ;
		TYPE_SET.add(boolean.class) ;
		TYPE_SET.add(float.class) ;
	}
	
	/**
	 * 判断对象是否基础类型
	 * @param obj
	 * @return
	 */
	public static  boolean isPrimitive(Object obj) {
		return TYPE_SET.contains(obj == null? null:obj);
	}
}
